package com.sinosoft.weixin.service.impl;

import com.sinosoft.weixin.db.entity.DictionaryExample;
import com.sinosoft.weixin.db.entity.MessageLog;
import com.sinosoft.weixin.db.entity.MessageTemplate;
import com.sinosoft.weixin.db.entity.MessageTemplateExample;
import com.sinosoft.weixin.db.mapper.DictionaryMapper;
import com.sinosoft.weixin.db.mapper.MessageLogMapper;
import com.sinosoft.weixin.db.mapper.MessageTemplateMapper;
import com.sinosoft.weixin.service.MessageService;
import com.sinosoft.weixin.service.ServiceException;
import com.sinosoft.weixin.service.mapper.DBSequenceMapper;
import com.sinosoft.weixin.shiro.ShiroUtils;
import com.sinosoft.weixin.shiro.UserPrincipal;
import com.sinosoft.weixin.utils.RandomUtil;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import org.apache.axis.message.SOAPHeaderElement;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.dom4j.DocumentHelper;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.output.XMLOutputter;
import org.springframework.beans.factory.annotation.Autowired;

import javax.xml.namespace.QName;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.net.URL;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

/**
 * Created by huzhengying on 2017/11/17.
 */
@org.springframework.stereotype.Service
public class MessageServiceImpl implements MessageService {
    private static org.apache.logging.log4j.Logger logger = LogManager.getLogger(MessageServiceImpl.class);
    @Autowired
    private DictionaryMapper dictionaryMapper;
    @Autowired
    private MessageLogMapper messageLogMapper;
    @Autowired
    private MessageTemplateMapper messageTemplateMapper;
    @Autowired
    private DBSequenceMapper dbSequenceMapper;

    @Override
    public void sendMessage(String workNumber,String messageCode, String mobile, Map<String,String> messageParams) {
        if(!isPhoneLegal(mobile)){
            throw new ServiceException("手机号不符合规则!");
        }

        String content = null;
        try{
            content = getMessageContent(messageCode,messageParams);
            logger.info(String.format(" %s 短信内容: %s ",workNumber,content));
        }catch (Exception ex){
            throw new ServiceException("获取短信内容失败!",ex);
        }

        try{
            MessageLog messageLog = new MessageLog();
            messageLog.setMakeDate(new Date());
            messageLog.setWorkNumber(workNumber);
            messageLog.setMessageContent(content);
            messageLog.setMessageCode(messageCode);
            messageLog.setMobile(mobile);
            String messageSid = getDBSequenceByTable("message_log",10);
            messageLog.setMessageSid(messageSid);
            messageLogMapper.insert(messageLog);

            //获取发送短信xml报文
            //String sendMessageDocument = getSendMessageDocument(messageCode,content,mobile,messageSid);
            //String sendMessageDocument = getDom4JString(messageCode,content,mobile,messageSid);
            String sendMessageDocument = "" +
                    "<?xml version=\"1.0\" encoding=\"gb2312\"?><Messages>    " +
                    "<PublicInfo>        " +
                    "<SystemCode>LIS</SystemCode>        " +
                    "<ServiceCode>%s</ServiceCode>        " +
                    "<User>38DE3D02818CB44864DB24FBDDC404BC</User>        " +
                    "<Password>325A2CC052914CEEB8C19016C091D2AC</Password>    " +
                    "</PublicInfo>    " +
                    "<Message>        " +
                    "<MessageId>%s</MessageId>        " +
                    "<MobileNums>            " +
                    "<MobileNum>%s</MobileNum>        " +
                    "</MobileNums>        " +
                    "<MessTopic/>        " +
                    "<SendData>%s</SendData>        " +
                    "<DataType>1</DataType>        " +
                    "<SendWay>0</SendWay>        " +
                    "<FixedDate/>        " +
                    "<FixedTime/>        " +
                    "<CustomerID/>        " +
                    "<InsuranceType>CS</InsuranceType>        " +
                    "<UnitCode>86</UnitCode>        " +
                    "<DealOrder>0</DealOrder>        " +
                    "<AnswerMatch>0</AnswerMatch>    " +
                    "</Message></Messages>";
            sendMessageDocument = String.format(sendMessageDocument,messageCode,messageSid,mobile,content);

            logger.info(String.format(" %s 传输报文内容 %s",workNumber,sendMessageDocument));

            //获取发送短信配置信息
            DictionaryExample dictionaryExample = new DictionaryExample();
            dictionaryExample.createCriteria().andTypeEqualTo("SEND-MESSAGE").andCodeEqualTo("SMURL");
            String smUrl = dictionaryMapper.selectByExample(dictionaryExample).get(0).getCodeValue();
            dictionaryExample = new DictionaryExample();
            dictionaryExample.createCriteria().andTypeEqualTo("SEND-MESSAGE").andCodeEqualTo("SMNAMESPACE");
            String smNameSpace = dictionaryMapper.selectByExample(dictionaryExample).get(0).getCodeValue();
            dictionaryExample = new DictionaryExample();
            dictionaryExample.createCriteria().andTypeEqualTo("SEND-MESSAGE").andCodeEqualTo("SMFUNC");
            String smFunc = dictionaryMapper.selectByExample(dictionaryExample).get(0).getCodeValue();

            //发送短信，获取返回结果
            String sendMessageResult = sendESBMessage(new Object[]{sendMessageDocument},smFunc,smUrl,smNameSpace);
            messageLog.setRemark(sendMessageResult);
            logger.info("调用短信平台返回结果!"+sendMessageResult);
            messageLogMapper.updateByPrimaryKey(messageLog);
        }catch (Exception ex){
            logger.error("发送失败！",ex);
            throw new ServiceException("系统异常!",ex);
        }

    }

    /**
     * 获取短信内容
     * @param messageCode 短信模版编码
     * @param messageParams 短信内容参数
     * @return
     */
    private String getMessageContent(String messageCode,Map<String,String> messageParams){
        if(!StringUtils.isNotBlank(messageCode)){
            throw new ServiceException("短信编码为空!");
        }

        MessageTemplateExample messageTemplateExample = new MessageTemplateExample();
        messageTemplateExample.createCriteria().andMessageCodeEqualTo(messageCode);
        List<MessageTemplate> messageTemplates = messageTemplateMapper.selectByExample(messageTemplateExample);
        if(messageTemplates.size() < 1){
            throw new ServiceException("短信模版为空!");
        }
        String messageTemplate = messageTemplates.get(0).getMessageTemplate();
        if(countRepetitionString(messageTemplate,"{") != messageParams.size()){
            throw new ServiceException("参数与模版参数个数不匹配!");
        }

        String content = messageTemplate;
        for(Map.Entry<String,String> messageParam : messageParams.entrySet()){
            String paramKey = "${"+messageParam.getKey()+"}";
            String paramValue = messageParam.getValue();
            content = content.replace(paramKey,paramValue);
        }
        if(content.contains("{")){
            throw new ServiceException("参数与模版参数不匹配");
        }

        //
        String messageContent = String.format("【国联人寿】验证码：%s，您正在使用手机帐号登入服务[验证码告知他人将导致帐号被盗，请勿泄露]",messageParams.get("verificationCode"));
        return messageContent;
    }

    /**
     * 判断字符串中某个字符的存在的数量
     * @param text 字符串
     * @param sub 某个字符
     * @return 个数
     */
    private int countRepetitionString(String text,String sub){
        int count =0, start =0;
        while((start=text.indexOf(sub,start))>=0){
            start += sub.length();
            count ++;
        }
        return count;
    }

    /**
     * 获取短信序列号,不满位数左补齐0
     * @param tableName
     */
    private String getDBSequenceByTable(String tableName,int index){
        if(StringUtils.isNotBlank(tableName)){
            String dbSequence = dbSequenceMapper.selectMaxId(tableName);
            if(StringUtils.isNotBlank(dbSequence)){
                dbSequence = StringUtils.leftPad(dbSequence,index,"0");
                return dbSequence;
            }else {
                return RandomUtil.getRandomNum(index);
            }
        }else {
            return RandomUtil.getRandomNum(index);
        }
    }

    private String getDom4JString(String messageCode,String content,String mobile,String messageSid) throws Exception{
        DictionaryExample dictionaryExample = new DictionaryExample();
        dictionaryExample.createCriteria().andTypeEqualTo("SEND-MESSAGE").andCodeEqualTo("SMUSER");
        String smUser = dictionaryMapper.selectByExample(dictionaryExample).get(0).getCodeValue();

        dictionaryExample = new DictionaryExample();
        dictionaryExample.createCriteria().andTypeEqualTo("SEND-MESSAGE").andCodeEqualTo("SMPASSWORD");
        String smPassword = dictionaryMapper.selectByExample(dictionaryExample).get(0).getCodeValue();

        org.dom4j.Document document = DocumentHelper.createDocument();
        org.dom4j.Element root = document.addElement("Messages");
        org.dom4j.Element PublicInfo = root.addElement("PublicInfo");
        org.dom4j.Element Message = root.addElement("Message");

        PublicInfo.addElement("SystemCode").addText("LIS");
        PublicInfo.addElement("ServiceCode").addText(messageCode);
        PublicInfo.addElement("User").addText(smUser);
        PublicInfo.addElement("Password").addText(smPassword);

        Message.addElement("MessageId").addText(messageSid);
        Message.addElement("MobileNums").addElement("MobileNum").addText(mobile);
        Message.addElement("MessTopic");
        Message.addElement("SendData").addText(content);
        Message.addElement("DataType").addText("1");
        Message.addElement("SendWay").addText("0");
        Message.addElement("FixedDate");
        Message.addElement("FixedTime");
        Message.addElement("CustomerID");
        Message.addElement("InsuranceType").addText("CS");
        Message.addElement("UnitCode").addText("86");
        Message.addElement("DealOrder").addText("1");
        Message.addElement("AnswerMatch").addText("0");

        OutputFormat xmlFormat = new OutputFormat();
        xmlFormat.setEncoding("gb2312");
        xmlFormat.setNewlines(true);
        xmlFormat.setIndent(true);
        xmlFormat.setIndent("    ");
        StringWriter stringWriter = new StringWriter();
        XMLWriter xmlWriter = new XMLWriter(stringWriter, xmlFormat);
        xmlWriter.write(document);

        String messageXml = stringWriter.toString().replaceAll("[^\\x20-\\x7e]", "");
        logger.info(messageXml);
        xmlWriter.close();

        return messageXml;
    }

    /**
     * 获取发送短息的报文
     * @param content 短信内容
     * @param mobile 手机号
     * @return String 返回报文
     */
    private String getSendMessageDocument(String messageCode,String content,String mobile,String messageSid) throws Exception{
        DictionaryExample dictionaryExample = new DictionaryExample();
        dictionaryExample.createCriteria().andTypeEqualTo("SEND-MESSAGE").andCodeEqualTo("SMUSER");
        String smUser = dictionaryMapper.selectByExample(dictionaryExample).get(0).getCodeValue();

        dictionaryExample = new DictionaryExample();
        dictionaryExample.createCriteria().andTypeEqualTo("SEND-MESSAGE").andCodeEqualTo("SMPASSWORD");
        String smPassword = dictionaryMapper.selectByExample(dictionaryExample).get(0).getCodeValue();

        Element root = new Element("Messages");
        Element publicInfo = new Element("PublicInfo");
        Element message = new Element("Message");
        root.addContent(publicInfo);
        root.addContent(message);
        Element systemCode = new Element("SystemCode");
        systemCode.setText("LIS");
        Element serviceCode = new Element("ServiceCode");
        serviceCode.setText(messageCode);
        Element user = new Element("User");
        user.setText(smUser);
        Element password = new Element("Password");
        password.setText(smPassword);

        publicInfo.addContent(systemCode);
        publicInfo.addContent(serviceCode);
        publicInfo.addContent(user);
        publicInfo.addContent(password);

        Element messageId = new Element("MessageId");
        messageId.setText(messageSid);
        Element mobileNums = new Element("MobileNums");
        Element mobileNum = new Element("MobileNum");
        mobileNum.setText(mobile);
        mobileNums.addContent(mobileNum);

        Element messTopic = new Element("MessTopic");
        Element sendData = new Element("SendData");
        sendData.setText(content);
        Element dataType = new Element("DataType");
        dataType.setText("1");
        Element sendWay = new Element("SendWay");
        sendWay.setText("0");
        Element fixedDate = new Element("FixedDate");
        Element fixedTime = new Element("FixedTime");
        Element customerID = new Element("CustomerID");
        Element insuranceType = new Element("InsuranceType");
        insuranceType.setText("CS");
        Element unitCode = new Element("UnitCode");
        unitCode.setText("86");
        Element dealOrder = new Element("DealOrder");
        dealOrder.setText("1");
        Element answerMatch= new Element("AnswerMatch");
        answerMatch.setText("0");

        message.addContent(messageId);
        message.addContent(mobileNums);
        message.addContent(messTopic);
        message.addContent(sendData);
        message.addContent(dataType);
        message.addContent(sendWay);
        message.addContent(fixedDate);
        message.addContent(fixedTime);
        message.addContent(customerID);
        message.addContent(insuranceType);
        message.addContent(unitCode);
        message.addContent(dealOrder);
        message.addContent(answerMatch);

        Document messageDocument = new Document(root);
        String sendMessageXml = transDocToString(messageDocument);

        return sendMessageXml;
    }

    /**
     * 核心调用esb方法
     * @param args 短信内容
     * @param function 方法名
     * @param url 地址
     * @param wsdlNameSpace 命名空间
     * @return String 调用解结果
     */
    private String sendESBMessage(Object[] args, String function, String url, String wsdlNameSpace) {
        Service service = new Service();
        Call call;
        String result = null;
        try {
            logger.info(String.format("fucction: %s ,url: %s ,wsdlNameSpace: %s ",function,url,wsdlNameSpace));
            call = (Call) service.createCall();
            call.setTargetEndpointAddress(new URL(url));
            call.setOperationName(new QName(wsdlNameSpace,function));
            SOAPHeaderElement header1 = new SOAPHeaderElement("","ClientId", "com.guolian.esb.lifeinsurance.lis");
            call.addHeader(header1);
            SOAPHeaderElement header2 = new SOAPHeaderElement("", "OperationCode","com.guolian.esb.lifeinsurance.sms.syssendservice.send");
            call.addHeader(header2);
            call.addParameter("arg0", org.apache.axis.encoding.XMLType.XSD_ANY, javax.xml.rpc.ParameterMode.IN);
            call.setReturnType(org.apache.axis.encoding.XMLType.XSD_STRING);
            result = (String) call.invoke(args);
        } catch (Exception e) {
            throw new ServiceException("调用短信接口平台失败!",e);
        }
        return result;
    }

    /**
     * Document转为字符串
     */
    public String transDocToString(Document doc){
        String rs = null;
        XMLOutputter xop = new XMLOutputter(" ", false, "gb2312");
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            xop.output(doc, bos);
            rs = bos.toString();
        } catch (IOException e) {
            logger.error(e);
        }

        return rs;
    }

    /**
     * 验证手机号，只校验是否为11位纯数
     * @param phone 手机号
     * @return true 正确 false 错误
     * @throws PatternSyntaxException
     */
    private boolean isPhoneLegal(String phone) throws PatternSyntaxException {
        Pattern pattern = Pattern.compile("[0-9]*");
        Matcher isNum = pattern.matcher(phone);
        if(isNum.matches() && phone.length() == 11){
            return true;
        }
        return false;
    }

}
